package client.noCifrado;

import java.io.BufferedReader;



import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.util.Random;

import org.bouncycastle.util.encoders.Base64;


/**
 * @author Mart�n Uribe Guti�rrez
 * @author Mauricio Pradilla Hosie.
 * Infraestructura Computacional
 * Universidad de los Andes
 * Algoritmos tomados de: http://www.java2s.com/Code/Java/Security/CatalogSecurity.htm
 * http://www.bouncycastle.org/
 * 
 * Clase que representa una empresa de recaudos de pensiones, que se conecta con el servidor 
 * de pensiones para enviar nuevos pagos de los empleados o para consultar, a trav�s de un
 * canal inseguro.
 */
public class ClienteSisPensionesNoCifrado extends Thread
{

	public static final int DELAY = 15;
	public static final int QUERY_QUANTITY = 1;

	//CONSTANTES PARA LA DEFINICION DEL PROTOCOLO
	/**
	 * Constante que indica el inicio de la fase 1
	 */
	public static final String HELLO = "HELLO";
	
	/**
	 * Constante para indicar el estado del proceso de guardado de archivos en batch
	 */
	public static final String STATUS = "STATUS";
	
	/**
	 * Constante que indica la recepci�n del archivo
	 */
	public static final String RECEIVED = "RECEIVED";
	
	/**
	 * Separador principal para los mensajes entre Cliente - Servidor
	 */
	public static final String SEPARADOR = ":";
	
	/**
	 * Constante para el separador secundario de los mensajes
	 */
	public static final String SEPARADOR2 = ";";

	/**
	 * Constante para el tipo de caracter
	 */
	public static final String COD = "US-ASCII";

	/**
	 * Constante que indica si una tarea fue exitosa
	 */
	public static final String OK = "OK";
	
	/**
	 * Constante del protocolo de comunicaci�n para informar un error en un punto del protocolo.
	 */
	public static final String DENEGADO = "DENEGADO";
	
	/**
	 * El m�ximo n�mero de grupos del curso
	 */
	public final static int MAX_NUM_GRUPOS = 65535; 
	
	/**
	 * Constante para un Host del servidor
	 */
	public static final String HOST = "infracomp.virtual.uniandes.edu.co";
	
	/**
	 * Constante para un Host del servidor
	 */
	public static final String HOST2 = "192.168.1.4";
	
	/**
	 * Puerto Inseguro por el cual se va a comunicarse con el servidor
	 */
	public static final int SOCKET = 1025;
	
	/**
	 * Constante del protocolo de comunicaci�n para solicitar el env�o de un archivo de pagos espec�fico
	 */
	private final static String RETRIEVE = "RETRIEVE";
	
	/**
	 * Constante que se utiliza para los estados en la fase 3 del protocolo
	 */
	public static final String UPDATE = "UPDATE";

	/**
	 * Constante para el grupo de trabajo, que representa el ID de una recaudadora
	 */
	public static final String GRUPO = "0";

	/**
	 * Socket de comunicacion con el servidor
	 */
	private Socket socket;
	
	/**
	 * Canal de lectura
	 */
	private BufferedReader reader;
	
	/**
	 * Canal de escritura
	 */
	private PrintWriter writer;

	/**
	 * Opci�n del prop�sito para la conexi�n. Opci�n 1: Actualizar informaci�n. Opci�n 2: Consultar documento
	 */
	private int opcion = 0;

	/**
	 * El nombre del archivo a consultar, dada por el usuario en consola
	 */
	private int nombreConsulta = 0;

	/**
	 * M�todo constructor de la clase que lleva a cabo toda la comunicaci�n con el servidor.
	 */
	public ClienteSisPensionesNoCifrado()
	{

	}

	/**
	 * M�todo main de la clase
	 * @param args
	 */
	public static void main(String[] args) {
		for (int i = 0; i < QUERY_QUANTITY; i++) {
			
		
		ClienteSisPensionesNoCifrado cst = new ClienteSisPensionesNoCifrado();
		cst.run();
		}
	}

	/**
	 * M�todo necesario para correr el hilo que desarrolla una de las transacciones
	 */
	public void run() {

		try {
			Thread.sleep(DELAY);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.crear();
	}

	/**
	 * M�todo responsable por llevar a cabo toda la comunicaci�n con el servidor
	 */
	public void crear()
	{
		try 
		{
			//BufferedReader br = new BufferedReader(new InputStreamReader(System.in) );
			/*System.out.println("Bienvenido al sistema de recaudos de pensiones. Para enviar un archivo presione '1' y luego 'Enter'" +
					". Para recuperar un archivo del servidor de pensiones presione '2' y luego 'Enter'.");*/
			opcion = 1;//Integer.parseInt(br.readLine( ));
			//System.out.println("Por favor indique el n�mero de grupo del archivo a enviar o recuperar, luego presione 'Enter'.");

			Socket generic = new Socket();
			Random generator = new Random();
			// crear Socket para realizar la conexi�n con el servidor
			int ran = 1;//generator.nextInt(1000)+30000;
			
			int serverPort = /*generator.nextInt(1000)+*/30001;
			nombreConsulta = ran;//Integer.parseInt(br.readLine( ));			
			if (!(opcion==1 || opcion==2) || !(0<=nombreConsulta && nombreConsulta<=MAX_NUM_GRUPOS))
			{
				System.out.println("Opci�n No v�lida:"+opcion);
			}
			else
			{
				socket = new Socket(HOST2, serverPort, generic.getLocalAddress(), ran);
				reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
				writer = new PrintWriter(socket.getOutputStream(), true);
				if (opcion == 1) // Si se seleccion� enviar un archivo
				{
					Generador gen = new Generador(nombreConsulta );
					System.out.println(HELLO+SEPARADOR+UPDATE+SEPARADOR+gen.darTamanoArchivo( )+SEPARADOR+nombreConsulta);
					byte[] mybytearray = new byte[(int) gen.darTamanoArchivo( )];
					File a = new File("dataNoCifrado/"+ran+".csv");
					try 
					{
						FileInputStream fileInputStream = new FileInputStream(a);
						fileInputStream.read(mybytearray);
					} catch (FileNotFoundException e) {
						System.out.println("File Not Found.");
						e.printStackTrace();
					}
					byte[] aEnviar = Base64.encode(mybytearray); //Contiene el archivo a enviar, en Base 64
					writer.println(HELLO+SEPARADOR+UPDATE+SEPARADOR+aEnviar.length+SEPARADOR+nombreConsulta); //Seg�n el protocolo
					char[] cha1 = new char[100000]; // Para recibir la respuesta del servidor
					reader.read(cha1);
					String mensaje = new String(cha1);
					String[] partes = mensaje.split(SEPARADOR);
					if( !partes[0].equals(STATUS) && !partes[1].equals(OK))
					{
						throw new Exception("No dijo OK:"+mensaje);
					}
					System.out.println("Servidor:"+mensaje);	
					DataOutputStream dos = new DataOutputStream(socket.getOutputStream( ));			
					dos.write(aEnviar); //Env�a al servidor el archivo a trav�s del DOS
					socket.getOutputStream( ).flush( );
					char[] cha = new char[100000];
					reader.read(cha);
					mensaje = new String(cha);
					String[] partes2 = mensaje.split(SEPARADOR);
					if (!partes2[0].equals(RECEIVED) && !partes2[1].equals(OK))
					{
						throw new Exception("No se recibi� el mensaje correctamente:" +mensaje);
					}
					else
					{
						System.out.println("Transacci�n Completada Exitosamente, el sistema de recaudos le desea un feliz d�a");
					}
				}
				else // Si se seleccion� recuperar un archivo
				{
					writer.println(HELLO+SEPARADOR+RETRIEVE+SEPARADOR+nombreConsulta);
					String respu = reader.readLine();
					String[] mnsj = respu.split(SEPARADOR);
					System.out.println("\n Servidor:"+respu);
					if(mnsj[1].equals(OK))
					{	
						//Recibir el documento
						byte[] arreglo = new byte[Integer.parseInt(mnsj[2])];		
						DataInputStream dis = new DataInputStream(socket.getInputStream( ));
						long length = socket.getInputStream( ).available( );
						if (length>Integer.MAX_VALUE)
						{
							System.out.println("El archivo es demasiado grande");
						}
						else
						{
							// Leer los bytes
							dis.readFully(arreglo); // arreglo contiene el arreglo de bytes enviado por el servidor a trav�s del socket
						}
						//Los bytes enviados se encuentran en base 64, por lo que se deben decodificar.
						byte[] arch = Base64.decode(arreglo);
						//Se crea el csv si este no existe
						File temp2 = new File("dataNoCifrado/archivo"+nombreConsulta+".csv");
						if (temp2.exists( ))
						{
							temp2.delete( );
						}
						FileOutputStream fos;
						fos = new FileOutputStream(temp2);					
						BufferedOutputStream bos = new BufferedOutputStream(fos);
						//Escribe el archivo en disco
						bos.write(arch,0,arch.length);
						writer.println(RECEIVED+SEPARADOR+OK);
						bos.flush();
						fos.flush();
						bos.close( );
						fos.close();
						System.out.println("Transacci�n Completada Exitosamente, el sistema de recaudos le desea un feliz d�a");
					}
				}
			}
		}
		catch (Exception e)
		{
			e.printStackTrace( );
			System.out.println(e.getMessage( ));
		}

	}

	/**
	 * Algoritmo de encapsulamiento a enteros. Convierte los bytes de un String a su representacion como enteros.
	 * @param b Los bytes a representar como enteros.
	 * @return EL string construido con la representacion de bytes como enteros.
	 */
	public String transformar( byte[] b )
	{
		String s=null;
		try {
			s = new String("".getBytes(),COD);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		for (int i = 0; i < b.length; i++) 
		{
			if( i == b.length-1 )
				s += Byte.toString(b[i]);
			else
				s += Byte.toString(b[i])+SEPARADOR2;
		}
		return s;
	}

	/**
	 * Algoritmo que transforma los enteros en los bytes correspondientes.
	 * @param s El string con los enteros a transformar.
	 * @param ss La cadena de peticion.
	 * @return Los bytes en su representacion real.
	 */
	public byte[] destransformar( String[] s, String ss )
	{
		System.out.println("destrans");
		byte[] b = new byte[s.length];

		for (int i = 0; i < b.length; i++) 
		{
			try {
				b[i] = Byte.parseByte(s[i],10);
			} catch (Exception e) {
				//e..printStackTrace();
				System.out.println(ss+SEPARADOR+e.getMessage());
			} 
		}		
		return b;
	}

}
